﻿using UnityEngine;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace DoomWad
{
	public class WadMapData
	{
		public mapvertex_t[] vertices;
		public maplinedef_t[] linedefs;
		public mapsector_t[] sectors;
		public mapsidedef_t[] sidedefs;
		public mapsubsector_t[] subsectors;
		public mapseg_t[] segs;
		public mapnode_t[] nodes;
		
		public int onlyLinedefVertexCount;
		public short skyCeilingHeight;
		public Dictionary<string, Texture> textures;

		
		public WadMapData()
		{
		}

		public Rect GetBoundingBox()
		{
			Rect bbox = new Rect();
			bbox.xMin = float.PositiveInfinity;
			bbox.yMin = float.PositiveInfinity;
			bbox.xMax = float.NegativeInfinity;
			bbox.yMax = float.NegativeInfinity;

			foreach ( mapvertex_t v in vertices )
				bbox = MathUtils.AddPointToRect( bbox, ( Vector2 ) v );

			return bbox;
		}

		public int GetSectorLinedefCount( int sectorIndex )
		{
			if ( sectorIndex == -1)
				return 0;

			int count = 0;
			for ( int i = 0; i < linedefs.Length; i++ )
			{
				maplinedef_t ld = linedefs[ i ];
				int rsector = ( ld.rsidenum != -1 ) ? ( int ) sidedefs[ ld.rsidenum ].sector : -1;
				int lsector = ( ld.lsidenum != -1 ) ? ( int ) sidedefs[ ld.lsidenum ].sector : -1;

				if ( rsector == sectorIndex || lsector == sectorIndex )
					count++;
			}

			return count;
		}

		/// <summary>
		/// Sector has one or two of edges
		/// </summary>
		/// <param name="sectorIndex"></param>
		/// <returns></returns>
		public bool IsSectorDegenerative( int sectorIndex )
		{
			return GetSectorLinedefCount( sectorIndex ) < 3;
		}

		public int GetSectorIndexForLinedef( int linedefIndex, bool rightSide )
		{
			int sidenum = rightSide ? linedefs[ linedefIndex ].rsidenum : linedefs[ linedefIndex ].lsidenum;
			if ( sidenum == -1 )
				return -1;
			return sidedefs[ sidenum ].sector;
		}

		//public Texture2D GetFloorTextureForLinedef( int linedefIndex, bool rightSide )
		//{
		//	int sidenum = rightSide ? linedefs[ linedefIndex ].rsidenum : linedefs[ linedefIndex ].lsidenum;
		//	if ( sidenum == -1 )
		//		return null;
		//	return GetFloorTextureForSector( sidedefs[ sidenum ].sector );
		//}

		//public Texture2D GetFloorTextureForSector( int sectorIndex )
		//{
		//	return textures[ sectors[ sectorIndex ].floorpic ] as Texture2D;
		//}

		public bool IsWall( int linedefIndex )
		{
			return ( linedefs[ linedefIndex ].flags & ( short ) LineDefFlags.ML_BLOCKING ) == ( short ) LineDefFlags.ML_BLOCKING;
		}

		public int FindClosestVertex( Vector2 point, float maxDistance = float.PositiveInfinity )
		{
			int cIndex = -1;
			float cDist2 = maxDistance;

			for ( int i = 0; i < vertices.Length; i++ )
			{
				Vector2 delta = point - ( Vector2 ) vertices[ i ];
				float d2 = delta.sqrMagnitude;
				if ( d2 < cDist2 )
				{
					cIndex = i;
					cDist2 = d2;
				}
			}

			return cIndex;
		}

		public int GetSectorByLinedef( int ldIndex, bool leftside, bool onlyNonDegenerative )
		{
			if ( ldIndex == -1 )
				return -1;

			maplinedef_t ld = linedefs[ ldIndex ];
			int sdIndex = leftside ? ld.lsidenum : ld.rsidenum;
			if ( sdIndex == -1 )
				return -1;

			int sIndex = ( int ) sidedefs[ sdIndex ].sector;
			if ( sIndex == -1 )
				return -1;

			if ( onlyNonDegenerative )
				if ( IsSectorDegenerative( sIndex ) )
					return -1;

			return sIndex;
		}

		public int FindSectorBySubSector( int ssIndex )
		{
			mapsubsector_t ss = subsectors[ ssIndex ];
			mapseg_t seg = segs[ ss.firstseg ];
			maplinedef_t ld = linedefs[ seg.linedef ];

			int rsector = ( ld.rsidenum != -1 ) ? ( int ) sidedefs[ ld.rsidenum ].sector : -1;
			int lsector = ( ld.lsidenum != -1 ) ? ( int ) sidedefs[ ld.lsidenum ].sector : -1;

			return seg.side == 0 ? rsector : lsector;
		}

		public int FindLindefByVertices( int v1, int v2, ref bool leftside )
		{
			for ( int i = 0; i < linedefs.Length; i++ )
			{
				maplinedef_t ld = linedefs[ i ];

				if ( ld.v1 == v1 && ld.v2 == v2 )
				{
					leftside = true;
					return i;
				}

				if ( ld.v1 == v2 && ld.v2 == v1 )
				{
					leftside = false;
					return i;
				}
			}

			return -1;
		}

		//----------------------------------------------------
		// DRAW UTILS
		//----------------------------------------------------

		public void DrawSegment( int vIndex1, int vIndex2, float normalScale )
		{
			mapvertex_t mv1 = vertices[ vIndex1 ];
			mapvertex_t mv2 = vertices[ vIndex2 ];

			Vector3 v1 = new Vector3( mv1.x, mv1.y );
			Vector3 v2 = new Vector3( mv2.x, mv2.y );

			Vector3 center = ( v2 + v1 ) * 0.5f;
			Vector3 normal = Vector3.Cross( v2 - v1, Vector3.forward ).normalized * normalScale;

			Gizmos.DrawLine( v1, v2 );
			Gizmos.DrawLine( center, center + normal );
		}

		public void DrawVertex( int vIndex, float scale )
		{
			mapvertex_t mv = vertices[ vIndex ];
			Vector3 v = new Vector3( mv.x, mv.y );
			Gizmos.DrawWireCube( v, Vector3.one * scale );
		}
	}
}